from collections import Counter

import ephem

from geoip.hosting import HOSTING_ISP, COLLEGE_ISP


def gather_node_information(args):  # gets all information available for a single node
    geoip, geoip2, node = args

    if node.get('Parents'):  # we don't export bootstrap nodes
        node['Parents'] = list(node['Parents'])
        node['Seen node IDs'] = list(node['Seen node IDs'])
        if geoip:  # add location infos
            location = extract_country_from_ip(geoip, node['IP address'])
            if location:
                node['Node City'] = location[0]
                node['Node Country'] = location[1]
            day_night_status = extract_day_night_status(geoip, node['IP address'])
            if day_night_status:
                node['Day/Night'] = day_night_status
            continent = extract_continent_from_isp(geoip, node['IP address'])
            if continent:
                node['Continent'] = continent
        if geoip2:  # add ISPs infos
            location = extract_isp_from_ip(geoip2, node['IP address'])
            if location:
                node['ISP'] = location
        return node
    return None


def extract_country_from_ip(geoip, ip):  # returns city and country given an IP
    try:
        res = geoip.get(ip)
        return [res['city']['names']['en'], res['country']['names']['en']]  # TODO add an option for changing language
    except (KeyError, TypeError):
        return None


def extract_day_night_status(geoip, ip):
    try:
        res = geoip.get(ip)
        return 'Day' if dayOrNight(str(res['location']['latitude']), str(res['location']['longitude'])) else 'Night'
    except (KeyError, TypeError, ephem.AlwaysUpError, ephem.NeverUpError):
        return None


def extract_isp_from_ip(geoip, ip):  # returns ISP given an IP
    try:
        res = geoip.get(ip)
        return res['autonomous_system_organization']
    except (KeyError, TypeError):
        return None


def extract_continent_from_isp(geoip, ip):  # returns continent given an IP
    try:
        res = geoip.get(ip)
        continent = which_continent(res['location']['latitude'], res['location']['longitude'])
        if continent != 'UNCLASSIFIED':
            return continent
        return None
    except (KeyError, TypeError, ephem.AlwaysUpError, ephem.NeverUpError):
        return None


def extract_country(data):
    return [list(v) for v in zip(*Counter([x['Node Country'] for x in data['up'] if 'Node Country' in x]).items())]


def extract_isp(data):
    return [list(v) for v in zip(*Counter([x['ISP'] for x in data['up'] if 'ISP' in x]).items())]


def extract_isp_stats(data):
    x = ['Hosting', 'College', 'Other']
    y = [0, 0, 0]
    for i in data['up']:
        if 'ISP' in i:
            t = i['ISP']
            if t in HOSTING_ISP:
                y[0] += 1
            elif t in COLLEGE_ISP:
                y[1] += 1
            else:
                y[2] += 1
    return x, y


def extract_day_night(data):
    y = [0, 0]
    for i in data['up']:
        if 'Day/Night' in i:
            y[0 if i['Day/Night'] == 'Day' else 1] += 1
    return ['Day', 'Night'], y


def extract_day_night_movement(data):
    x = [0]
    y = [[0, 0]]  # number of day, night nodes
    y_ = [0]
    dict_ = {}  # extracting up nodes and fulling a dictionnary
    for i in data['up']:
        try:
            dict_[i['Node ID']] = [i['Up'], i['Day/Night']]
        except KeyError:
            pass
    number = 0
    for k, v in sorted(dict_.items(), key=lambda x: x[1]):  # sort the dictionnary by up time
        number += 1
        x.append(number)
        y_dernier = y[-1]
        a, b = y_dernier[0], y_dernier[1]
        if v[1] == 'Night':
            b += 1
            y.append([a, b])  # updating number
        else:
            a += 1
            y.append([a, b])  # updating number
        y_dernier = y[-1]
        y_.append((y_dernier[0] / (y_dernier[1] + y_dernier[0])) * 100)  # updating stats
    y = y_

    return x, y


def extract_continent(data):
    y = [0] * nb_of_continent
    for i in data['up']:
        if 'Continent' in i:
            t = i['Continent']
            y[continent_list.index(t)] += 1

    return continent_list, y


def dayOrNight(latitude, longitude):  # returns 0 if it's day time or 1 if night time
    user = ephem.Observer()
    user.lat = latitude
    user.lon = longitude
    user.elevation = 4
    user.temp = 20  # current air temperature gathered manually
    user.pressure = 1019.5  # current air pressure gathered manually

    return user.next_setting(ephem.Sun()).datetime() < user.next_rising(ephem.Sun()).datetime()


CONTINENTS = {"AMERICA": {"latitude": (90, -59.175928), "longitude": (-167.856892, -24.349205)},
              "EUROPE": {"latitude": (68.911005, 35.029996), "longitude": (-22.666353, 30.176383)},
              "OCEANIA": {"latitude": (11.307708, -54.085173), "longitude": (83.671875, 197.578125)},
              "MIDDLE_EAST": {"latitude": (43.675818, 13.368243), "longitude": (26.718750, 66.875)},
              "AFRICA": {"latitude": (33.833920, -37.055177), "longitude": (-23.906250, 52.031250)},
              "ASIA": {"latitude": (77.645947, 10.617418), "longitude": (35.859375, 191.250000)}
              }
continent_list = list(CONTINENTS.keys())
nb_of_continent = len(CONTINENTS)


def which_continent(latitude, longitude):  # find which continent corresponds to a given latitude, longitude
    for continent, coords in CONTINENTS.items():
        if coords["longitude"][0] > longitude > coords["longitude"][1] and coords["latitude"][0] > latitude > \
                coords["latitude"][1]:
            return continent
    return 'UNCLASSIFIED'